/**
 * RPC Servlet
 */
package com.appspot.rsp.rpc;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

/**
 * @author pham
 * 
 */
public class RspServlet extends HttpServlet {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private static final Logger log = Logger.getLogger(RspServlet.class.getName());

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		log.info(req.toString());
		Object result = new Object();
		try {
			result = handleRequest(req);
			resp.setContentType("application/json");
			resp.setCharacterEncoding("UTF-8");
			resp.getWriter().write(new Gson().toJson(result));
			log.info(result.toString());
		} catch (Exception e) {
			log("Handle RPC Exception", e);
		}
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		log.info(req.toString());
		Object result = new Object();
		try {
			result = handleRequest(req);
			resp.setContentType("application/json");
			resp.setCharacterEncoding("UTF-8");
			resp.getWriter().write(new Gson().toJson(result));
			log.info(result.toString());
		} catch (Exception e) {
			log("Handle RPC Exception", e);
		}
	}

	/**
	 * Accept request, then process and forward request to compatible service.
	 * Example of request data: { "id": 1, "service": "net.rsp.dao.TabDao",
	 * "method": "getTabByKey", "parameters": [ "123", "" ] }
	 * 
	 * @param req
	 *            Request
	 * @return Return value of executing compatible service.
	 * @throws Exception
	 */
	private Object handleRequest(HttpServletRequest req) throws Exception {
//		// for qooxdoo request
//		InputStream is = req.getInputStream();
//		Reader reader = new InputStreamReader(is, "UTF-8");
//		StringBuffer requestBuffer = new StringBuffer();
//		char[] readBuffer = new char[8192];
//		int length;
//		while ((length = reader.read(readBuffer)) != -1) {
//			requestBuffer.append(readBuffer, 0, length);
//		}
//		String requestString = requestBuffer.toString();
		String reqJson = req.getParameter("json");
		JsonObject jso = new JsonParser().parse(reqJson).getAsJsonObject();
		String reqService = jso.get("service").getAsString();
		String reqMethod = jso.get("method").getAsString();
		List<Object> list = new ArrayList<Object>();
		Iterator<JsonElement> iter = jso.get("parameters").getAsJsonArray().iterator();
		while (iter.hasNext()){
			list.add(iter.next().getAsString());
		}
		Object[] reqParameters = {list.toArray()};	// {}
//		search compatible service and invoke
		Class<?> clazz = Class.forName(reqService);
		Method[] methods = clazz.getDeclaredMethods();
		Method candidate = null;
		Class<?>[] methodParameterTypes;
		for (Method method : methods) {
			candidate = method;
			if (!candidate.getName().equals(reqMethod)) {
				candidate = null;
				continue;
			}
			methodParameterTypes = candidate.getParameterTypes();
			if (methodParameterTypes.length != reqParameters.length) {
				candidate = null;
				continue;
			}
			break;
		}
		if (candidate == null) { // not found
			throw new NoSuchMethodException("No method "+reqMethod+" takes "+reqParameters.length+" parameters.");
		}
		return candidate.invoke(clazz.newInstance(), reqParameters);
	}
}
